package com.by.rpc.factory;

import com.by.log.Logger;
import com.by.log.LoggerFactory;
import com.by.rpc.dto.RpcRequest;
import com.by.rpc.dto.RpcResponse;
import com.by.rpc.enums.ParameterPosition;
import com.by.rpc.exception.BusinessException;
import com.by.rpc.netty.RpcClient;
import com.by.rpc.util.ServiceParameter;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * RPC代理工厂，用于创建服务接口的动态代理
 */
public class RpcProxyFactory implements InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(RpcProxyFactory.class);

    private final Class<?> serviceClass;
    private final String version;
    private final String group;
    private final String host;
    private final int port;
    private final String serviceName;


    public RpcProxyFactory(Class<?> serviceClass, String modelName, String version, String group, String host, int port) {
        this.serviceClass = serviceClass;
        this.version = version;
        this.group = group;
        this.host = host;
        this.port = port;
        this.serviceName = modelName;
    }

    /**
     * 创建服务代理对象
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(
                serviceClass.getClassLoader(),
                new Class<?>[]{serviceClass},
                this
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
        }

        // 如果方法返回类型是 CompletableFuture，则执行异步调用
        if (CompletableFuture.class.isAssignableFrom(method.getReturnType())) {
            return invokeAsync(proxy, method, args);
        } else {
            // 同步方法，阻塞等待结果
            logger.warn("Synchronous RPC call for {}.{}(), this might cause blocking.",
                    serviceClass.getName(), method.getName());
            CompletableFuture<Object> future = invokeAsync(proxy, method, args);
            try {
                // 这里应该从配置中获取超时时间
                return future.get(5, TimeUnit.SECONDS);
            } catch (ExecutionException e) {
                throw e.getCause(); // 抛出原始异常
            } catch (Exception e) {
                throw new RuntimeException("Synchronous RPC call failed", e);
            }
        }
    }

    /**
     * 异步方法调用
     */
    public CompletableFuture<Object> invokeAsync(Object proxy, Method method, Object[] args) {
        logger.debug("Invoking remote method async: {}.{}(), parameters: {}",
                serviceClass.getName(), method.getName(), args);

        List<ServiceParameter> parameters = new ArrayList<>();
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                parameters.add(new ServiceParameter(i,
                        "args" + i,
                        arg != null ? arg.getClass().getName() : Object.class.getName(),
                        ParameterPosition.RPC.getValue(),
                        arg,
                        null));
            }
        }

        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setRequestId(UUID.randomUUID().toString());
        rpcRequest.setInterfaceName(serviceClass.getName());
        rpcRequest.setVersion(version);
        rpcRequest.setGroup(group);
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setParameters(parameters);

        return RpcClientFactory.getInstance().getClientAsync(host, port)
                .thenCompose(client -> {
                    return client.sendRequest(rpcRequest)
                            .thenApply(rpcResponse -> {
                                RpcClientFactory.getInstance().returnClient(host, port, client);
                                if (rpcResponse.getStatus() == RpcResponse.ResponseStatus.SUCCESS) {
                                    return rpcResponse.getData();
                                } else {
                                    throw new CompletionException(new RuntimeException("RPC调用失败: " + rpcResponse.getErrorMsg()));
                                }
                            })
                            .exceptionally(ex -> {
                                RpcClientFactory.getInstance().returnClient(host, port, client);
                                throw new CompletionException("RPC调用异常", ex);
                            });
                });
    }
}